#include "EscenarioModelo.h"
#include "PosicionBackTracking.h"

#include "Fantasma.h"

EscenarioModelo* EscenarioModelo::instanciaUnica = NULL;
unsigned int EscenarioModelo::factorMapeoSendero = 3;

EscenarioModelo::EscenarioModelo(){
    this->fantasmasJugador1 = new Lista<ObjetoVivo>();
    this->fantasmasJugador2 = new Lista<ObjetoVivo>();
    //this->coleccionBonus = new Lista<ObjetoVivo>();
	this->bonus = NULL;
    this->colisionEntrePacmansActiva = false;
    this->pacman1 = NULL;
    this->pacman2 = NULL;
    this->sendero = NULL;
	this->escenarioBackTracking = new EscenarioBackTracking();
}	
    
EscenarioModelo* EscenarioModelo::getEscenario(){
    if(EscenarioModelo::instanciaUnica == NULL)
        EscenarioModelo::instanciaUnica = new EscenarioModelo();
    return EscenarioModelo::instanciaUnica;
}

void EscenarioModelo::generarSendero(Grilla* grilla){
	this->sendero = new Sendero(grilla,factorMapeoSendero);
	this->filas = grilla->getFilas();
	this->columnas = grilla->getColumnas();
}

Sendero* EscenarioModelo::getSendero(){
	return this->sendero;
}

void EscenarioModelo::vivirEscenario(){
    /*
    El metodo itera por las colecciones, si el objeto esta vivo lo hace vivir, de lo contrario lo 
    saca de la caoleccion.
    */	
    IteradorLista<ObjetoVivo> it = this->fantasmasJugador1->iterador();
    while(it.tieneSiguiente()){
        ObjetoVivo* ov = it.siguiente();
        if(ov->estaVivo()){
            ov->vivir();
        }else{
            this->fantasmasJugador1->quitar(ov);
        }
    }

	it = this->fantasmasJugador2->iterador();
    while(it.tieneSiguiente()){
        ObjetoVivo* ov = it.siguiente();
        if(ov->estaVivo()){
            ov->vivir();
        }else{
            this->fantasmasJugador1->quitar(ov);
            }
        }
	if(this->bonus != NULL){
		if(((ObjetoVivo*)this->bonus)->estaVivo()){
            this->bonus->vivir();
		}
	}
	
    
	//it = this->coleccionBonus->iterador();
    //while(it.tieneSiguiente()){
    //    ObjetoVivo* ov = it.siguiente();
    //    if(ov->estaVivo()){
    //        ov->vivir();
    //    }else{
			// ESTO HACIA Q PINCHe
			///////////////////////
            //this->coleccionBonus->quitar(ov);
     //   }
    //}	

    if(this->pacman1->estaVivo()) 
		this->pacman1->vivir();
    if(this->pacman2->estaVivo()) 
		this->pacman2->vivir();

}

bool EscenarioModelo::perteneceAlSendero(Punto* punto){
    return this->sendero->perteneceAlSendero(punto);
}

BonusModel* EscenarioModelo::getBonusDelEscenario(){

	return this->bonus;
}

void EscenarioModelo::agregarBonus(BonusModel* bonus){
    //this->coleccionBonus->agregar(bonus);
	this->bonus = bonus;
}


void EscenarioModelo::chequearColisionFantasmaPacman(Fantasma* unFantasma, Pacman* unPacman){

	// Verifico si el pacman esta vivo y si hubo colision
	if( unPacman->estaVivo() && unPacman->colisioneCon(unFantasma)){
		// Verifico si es la presa actual o no.
		if( unFantasma->getPresa()->equals(unPacman) ){
			this->contColisionesFantasmaPacmanEnemigo++;
			// Con una vez alcanza en un "vivir" 
			if( !this->colisionPacmanEnemigo ){	
				this->colisionPacmanEnemigo = true ;
				unFantasma->colisionarConPacman(unPacman);
			}
		}else{
			this->contColisionesFantasmaPacmanAmigo++;
		}
	}


}

void EscenarioModelo::chequearColisiones(){
    /*
    Chequeo de Colision entre los Jugadores.
    */
	if(this->pacman1->estaVivo() && this->pacman2->estaVivo() && this->pacman1->colisioneCon(this->pacman2)){
		if(!this->colisionEntrePacmansActiva){
			// Para evitar repetir continuas colisiones
			this->colisionEntrePacmansActiva = true;
			this->pacman1->colisionarConPacman(this->pacman2);
		}
	}else{
		this->colisionEntrePacmansActiva = false;
	}

    /*
    Chequeo de colision entre los Jugadores y los Bonuses. 
    */
	this->colisionEntreBonusPacman = false;
	int cantColisionBonusPacman = 0;
    //IteradorLista<ObjetoVivo> itBonus = this->coleccionBonus->iterador();
    //while(itBonus.tieneSiguiente()){
    //    ObjetoVivo* unBonus = itBonus.siguiente();
	ObjetoVivo* unBonus = this->bonus;
	if(unBonus != NULL){
		if(unBonus->estaVivo()){
			if(this->pacman1->estaVivo() && this->pacman1->colisioneCon(unBonus) ){
	            unBonus->colisionarConPacman(this->pacman1);
				cantColisionBonusPacman++;
				if (!this->colisionEntreBonusPacman){
					this->colisionEntreBonusPacman = true;
				}
			}else if(this->pacman2->estaVivo() && this->pacman2->colisioneCon(unBonus)){
	            unBonus->colisionarConPacman(this->pacman2);
				cantColisionBonusPacman++;
					if (!this->colisionEntreBonusPacman){
					this->colisionEntreBonusPacman = true;
				}
			}
		}
	//}
    }
    
	//this->colisionEntreBonusPacman = cantColisionBonusPacman;

	this->contColisionesFantasmaPacmanEnemigo = 0;
	this->contColisionesFantasmaPacmanAmigo = 0;
	this->colisionPacmanAmigo = false;
	this->colisionPacmanEnemigo = false;
    
	/*
    Chequeo de colision entre los Jugadores y los fantasmas del Pacman 1. 
    */
    IteradorLista<ObjetoVivo> itFantasmasJugador1 = this->fantasmasJugador1->iterador();
    while(itFantasmasJugador1.tieneSiguiente()){

        Fantasma* unFantasma = (Fantasma*)itFantasmasJugador1.siguiente();
        this->chequearColisionFantasmaPacman(unFantasma, (Pacman*)this->pacman1);
		this->chequearColisionFantasmaPacman(unFantasma, (Pacman*)this->pacman2);
    }

    /*
    Chequeo de colision entre los Jugadores y los fantasmas del Pacman 2. 
    */
    IteradorLista<ObjetoVivo> itFantasmasJugador2 = this->fantasmasJugador2->iterador();
    while(itFantasmasJugador2.tieneSiguiente()){
        Fantasma* unFantasma = (Fantasma*)itFantasmasJugador2.siguiente();
        this->chequearColisionFantasmaPacman(unFantasma, (Pacman*)this->pacman1);
		this->chequearColisionFantasmaPacman(unFantasma, (Pacman*)this->pacman2);
	}

    /* 
    Chequeo de las colisiones entre fantasmas del pacman 1.
    */
    itFantasmasJugador1 = this->fantasmasJugador1->iterador();
    while(itFantasmasJugador1.tieneSiguiente())
        {
        ObjetoVivo* unFantasma = itFantasmasJugador1.siguiente();
        IteradorLista<ObjetoVivo> otroItFantasmasJugador1 = this->fantasmasJugador1->iterador();
        while(otroItFantasmasJugador1.tieneSiguiente()){
            ObjetoVivo* otroFantasma = otroItFantasmasJugador1.siguiente();
            if(otroFantasma != unFantasma && unFantasma->colisioneCon(otroFantasma))
                unFantasma->colisionarConFantasma(otroFantasma);
            }
        }

    /* 
    Chequeo de las colisiones entre fantasmas del pacman 2.
    */
    itFantasmasJugador2 = this->fantasmasJugador2->iterador();
    while(itFantasmasJugador2.tieneSiguiente())
        {
        ObjetoVivo* unFantasma = itFantasmasJugador2.siguiente();
        IteradorLista<ObjetoVivo> otroItFantasmasJugador2 = this->fantasmasJugador2->iterador();
        while(otroItFantasmasJugador2.tieneSiguiente()){
            ObjetoVivo* otroFantasma = otroItFantasmasJugador2.siguiente();
            if(otroFantasma != unFantasma && unFantasma->colisioneCon(otroFantasma))
                unFantasma->colisionarConFantasma(otroFantasma);
            }
        }
}

void EscenarioModelo::setPacmanJugador1(Pacman* unPacman){
    this->pacman1 = unPacman;
}

bool EscenarioModelo::huboColisionEntrePacmans(){
	return this->colisionEntrePacmansActiva;
}

bool EscenarioModelo::huboColisionFantasmaPacmanAmigo(){
	return (this->contColisionesFantasmaPacmanAmigo != 0 );
}

bool EscenarioModelo::huboColisionFantasmaPacmanEnemigo(){
	return (this->contColisionesFantasmaPacmanEnemigo != 0 );
}

bool EscenarioModelo::huboColisionEntreBonusPacman(){
	return this->colisionEntreBonusPacman;
}

void EscenarioModelo::setPacmanJugador2(Pacman* unPacman){
    this->pacman2 = unPacman;
}

Pacman* EscenarioModelo::getPacmanJugador1(){
    return this->pacman1;
}

Pacman* EscenarioModelo::getPacmanJugador2(){
    return this->pacman2;
}

int EscenarioModelo::getFilas(){
	return this->filas;
}

int EscenarioModelo::getColumnas(){
	return this->columnas;
}


Lista<ObjetoVivo>* EscenarioModelo::getFantasmasJugador1(){
    return this->fantasmasJugador1;
    
    }


Lista<ObjetoVivo>* EscenarioModelo::getFantasmasJugador2(){
    
    return this->fantasmasJugador2;
    }

void EscenarioModelo::addFantasmasJugador1(ObjetoVivo* unFantasma){
	this->fantasmasJugador1->agregar(unFantasma);
}

void EscenarioModelo::addFantasmasJugador2(ObjetoVivo* unFantasma){
	this->fantasmasJugador2->agregar(unFantasma);
}

Punto* EscenarioModelo::getPuntoMedioDelBonus(){

        Punto* punto = 0;

		Punto* pacmanUnoSendero = this->pacman1->getPosicion();
		Punto* pacmanDosSendero = this->pacman2->getPosicion();

		// Si estan en la misma posicion retorno nulo.
		// Cualquier punto que elija tiene la misma distancia a ambos
		// podria elegir un pto al azar 
		int radio = getSendero()->getRadioStandard();
		if(pacmanUnoSendero->equals(pacmanDosSendero) || pacmanUnoSendero->distanciaCon(pacmanDosSendero) <= (2*radio))
			return 0;

		Punto* pacmanUnoGrilla = EscenarioModelo::getEscenario()->getSendero()->getPuntoGrilla(pacmanUnoSendero);
		Punto* pacmanDosGrilla = EscenarioModelo::getEscenario()->getSendero()->getPuntoGrilla(pacmanDosSendero);

		PosicionBackTracking* pacmanUno = new PosicionBackTracking(pacmanUnoGrilla->getY(),pacmanUnoGrilla->getX());
        PosicionBackTracking* pacmanDos = new PosicionBackTracking(pacmanDosGrilla->getY(),pacmanDosGrilla->getX());
        
		////Punto medio inicial del bonus este punto es arbitrario.
        //PosicionBackTracking bonus(7,8);
        //PosicionBackTracking* medio = this->escenarioBackTracking->igualarPosicionDelBonusEntrePacman(&pacmanUno,&pacmanDos,&bonus);
        PosicionBackTracking* medio = this->escenarioBackTracking->igualarPosicionDelBonusEntrePacman(pacmanUno,pacmanDos);
		if(medio){
			punto = new Punto(0,0);
			punto->setX(medio->getColumna());
			punto->setY(medio->getFila());
		}
		
		//liberar recursos
		delete pacmanUnoGrilla;
		delete pacmanDosGrilla;
		delete pacmanUno;
		delete pacmanDos;

		delete medio;

        return punto;
}

int EscenarioModelo::getRadioStandard(){
	return this->getSendero()->getRadioStandard();
}

EscenarioModelo::~EscenarioModelo(){
	delete this->escenarioBackTracking;
	delete this->pacman1;
	delete this->pacman2;
    delete this->sendero;
	delete this->fantasmasJugador1;
	delete this->fantasmasJugador2;
    //delete this->coleccionBonus;
}

void EscenarioModelo::destroy(){
	delete instanciaUnica;
	instanciaUnica = NULL;
}
